import csv
import pandas as pd

from core.management.commands.seed import (SEED_USERNAME, SEED_PASSWORD,
                                           SEED_USERNAME2, SEED_PASSWORD2,
                                           SEED_FILE_PATH)
from core.models import Queue, Profile, ProjectPermissions
from core.utils.utils_queue import fill_queue


class HashableDict(dict):
    # Need a hashable dict so we can put them in a set
    def __hash__(self):
        return hash(frozenset(self))


def assert_obj_exists(model, filter_):
    '''
    See if an instance of the given model matching the given filter
    dict exists.
    '''
    matching_count = model.objects.filter(**filter_).count()
    assert matching_count > 0, "{} matching filter {} " \
        "does not exist. ".format(model.__name__, filter_)


def assert_redis_matches_db(test_redis):
    '''
    Make sure all nonempty queues are present in the redis DB and
    have the correct amount of data, as determined by the DB.
    '''
    for q in Queue.objects.all():
        data_count = q.data.count()

        if data_count > 0:
            assert test_redis.exists('queue:' + str(q.pk))
            assert test_redis.llen('queue:' + str(q.pk)) == data_count
            assert test_redis.exists('set:' + str(q.pk))
            assert test_redis.scard('set:' + str(q.pk)) == data_count
        else:
            # Empty lists don't exist in redis
            assert not test_redis.exists('queue:' + str(q.pk))
            assert not test_redis.exists('set:' + str(q.pk))


def read_test_data_api(file=SEED_FILE_PATH):
    '''
    Read the test data from its file and store as list of dicts.  Used for API
    tests.
    '''
    with open(file) as f:
        return [{'Text': d['Text'], 'Label': d['Label']} for d in csv.DictReader(f)]


def read_test_data_backend(file=SEED_FILE_PATH):
    '''
    Read the test data from its file and store as dataframe.  Used for backend
    tests.
    '''
    return pd.read_csv(file)


def subset_keys(dict_, keys):
    '''
    Return a new HashableDict with the given subset of keys extracted from
    the given dict.
    '''
    return HashableDict([(k, dict_[k]) for k in dict_ if k in keys])


def assert_collections_equal(expected, actual):
    '''
    Determine whether two collections contain the same objects.
    '''
    for obj in expected:
        assert obj in actual

    for obj in actual:
        assert obj in expected


def compare_post_response(response, expected, significant_keys):
    '''
    Compare an HttpResponse generated by client.post(<url>, <data>)
    to an expected result.  Only fields listed in "significant_keys"
    will be considered in equality comparison.
    '''
    assert response.status_code == 201

    significant_expected = subset_keys(expected, significant_keys)
    significant_response = subset_keys(response.json(), significant_keys)

    assert significant_expected == significant_response


def compare_get_response(response, expected, significant_keys):
    '''
    Compare a django.http.HttpResponse generated by a client.get(<url>)
    to a list of expected API results.  Only fields listed in "significant_keys"
    will be considered in equality comparisons.
    '''
    assert response.status_code == 200

    response_json = response.json()

    significant_keys = set(significant_keys)

    significant_expected = set(subset_keys(d, significant_keys)
                               for d in expected)
    significant_response = set(subset_keys(d, significant_keys)
                               for d in response_json['results'])

    assert_collections_equal(significant_expected, significant_response)


def sign_in_and_fill_queue(project, test_queue, client, admin_client=None):
    fill_queue(test_queue, 'random')

    if admin_client is not None:
        admin_client.login(username=SEED_USERNAME2, password=SEED_PASSWORD2)
        admin_profile = Profile.objects.get(user__username=SEED_USERNAME2)
        ProjectPermissions.objects.create(profile=admin_profile,
                                          project=project,
                                          permission='ADMIN')
    else:
        admin_profile = None
    client.login(username=SEED_USERNAME, password=SEED_PASSWORD)

    client_profile = Profile.objects.get(user__username=SEED_USERNAME)

    ProjectPermissions.objects.create(profile=client_profile,
                                      project=project,
                                      permission='CODER')

    return client_profile, admin_profile
